{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 本地调用tool工具-调用Agent执行本地命令\n",
    "\n",
    "部分开发者需要给Client应用提供与本地命令交互的能力，比如在Client应用中调用Agent执行本地命令。并实现数据或文件的处理\n",
    "\n",
    "##### 配置运行环境&导入Client应用"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "import appbuilder\n",
    "\n",
    "# AppBuilder Token，替换为您个人的Token\n",
    "os.environ[\"APPBUILDER_TOKEN\"] = \"your api key\"\n",
    "\n",
    "# 应用为：智能问题解决者\n",
    "app_id = \"b9473e78-754b-463a-916b-f0a9097a8e5f\"\n",
    "app_client = appbuilder.AppBuilderClient(app_id)\n",
    "conversation_id = app_client.create_conversation()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### 创建一个能执行本地bash命令的tool工具"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 赋予应用一个执行本地命令的能力\n",
    "def execute_local_command(cmd: str):\n",
    "    import subprocess\n",
    "    try:\n",
    "        result = subprocess.check_output(cmd, shell=True).decode(\"utf-8\")\n",
    "        if result.strip() == \"\":\n",
    "            return \"命令执行成功，无返回值\"\n",
    "        return result\n",
    "    except Exception as e:\n",
    "        return str(e)\n",
    "\n",
    "\n",
    "# 创建工具的描述：json_schema格式\n",
    "tools = [\n",
    "    {\n",
    "        \"type\": \"function\",\n",
    "        \"function\": {\n",
    "            \"name\": \"execute_local_command\",\n",
    "            \"description\": \"可以在bash环境中，执行输入的指令，注意，一次只能执行一个原子命令。例如：ls\",\n",
    "            \"parameters\": {\n",
    "                \"type\": \"object\",\n",
    "                \"properties\": {\n",
    "                    \"cmd\": {\n",
    "                        \"type\": \"string\",\n",
    "                        \"description\": \"需要执行的指令\",\n",
    "                    },\n",
    "                },\n",
    "                \"required\": [\"cmd\"],\n",
    "            },\n",
    "        },\n",
    "    }\n",
    "]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 我们再次询问应用，并给他赋予这个能力"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "message_1 = app_client.run(\n",
    "    conversation_id=conversation_id,\n",
    "    query=\"请问当前文件夹下有哪些文件？如果没有test.txt文件，请新建一个test.txt文件，内容为：Hello World！\",\n",
    "    tools=tools\n",
    ")\n",
    "print(\"Agent的中间思考过程：\")\n",
    "print(message_1.content.events[-1].model_dump_json(indent=4))\n",
    "print(\"Agent思考结束，等待我们上传本地结果\\n\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**output**\n",
    "\n",
    "- Agent的中间思考过程\n",
    "\n",
    "```\n",
    "Agent的中间思考过程：\n",
    "{\n",
    "    \"code\": 0,\n",
    "    \"message\": \"\",\n",
    "    \"status\": \"interrupt\",\n",
    "    \"event_type\": \"Interrupt\",\n",
    "    \"content_type\": \"contexts\",\n",
    "    \"detail\": {\n",
    "        \"text\": {\n",
    "            \"function_call\": {\n",
    "                \"thought\": \"首先，我需要使用execute_local_command工具来执行ls命令，列出当前文件夹下的所有文件。然后，我需要检查输出中是否存在test.txt文件。如果不存在，我将再次使用execute_local_command工具来执行echo命令，创建一个新的test.txt文件，并写入内容'Hello World！'。\",\n",
    "                \"name\": \"execute_local_command\",\n",
    "                \"arguments\": {\n",
    "                    \"cmd\": \"ls\"\n",
    "                },\n",
    "                \"usage\": {\n",
    "                    \"prompt_tokens\": 718,\n",
    "                    \"completion_tokens\": 111,\n",
    "                    \"total_tokens\": 829,\n",
    "                    \"name\": \"ERNIE-4.0-Turbo-8K\",\n",
    "                    \"type\": \"plan\"\n",
    "                },\n",
    "                \"tool_call_id\": \"f60b241c-ddda-46aa-a2a6-b8bf1da2a424\"\n",
    "            },\n",
    "            \"used_tool\": []\n",
    "...\n",
    "    ]\n",
    "}\n",
    "Agent思考结束，等待我们上传本地结果\n",
    "```\n",
    "\n",
    "- 大模型下发了调用本地函数的参数，我们使用这个参数调用本地函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "tool_call = message_1.content.events[-1].tool_calls[-1]\n",
    "tool_call_id = tool_call.id\n",
    "tool_call_argument = tool_call.function.arguments\n",
    "local_func_result = execute_local_command(**tool_call_argument)\n",
    "print(\"No.1 local_func_result:\\n {}\\n\".format(local_func_result))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**output**\n",
    "```\n",
    "No.1 local_func_result:\n",
    "multi_tool_call.ipynb\n",
    "multi_tool_call.py\n",
    "multi_tool_call_with_handler.py\n",
    "sdk_ knowledgebase.ipynb\n",
    "sdk_trace.ipynb\n",
    "simple_tool_call.ipynb\n",
    "simple_tool_call.py\n",
    "tmp.log\n",
    "黑神话(悟空).pdf\n",
    "```\n",
    "\n",
    "- 向应用返回本地运行的结果，继续等待Agent的思考"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "message_2 = app_client.run(\n",
    "    conversation_id=conversation_id,\n",
    "    tool_outputs=[{\n",
    "        \"tool_call_id\": tool_call_id,\n",
    "        \"output\": local_func_result\n",
    "    }]\n",
    ")\n",
    "\n",
    "event_status = message_2.content.events[-1].status\n",
    "if event_status != \"interrupt\":\n",
    "    print(\"Agent 的回答是：\\n\")\n",
    "    print(message_2.content.answer)\n",
    "    quit()\n",
    "\n",
    "\n",
    "print(\"Agent的中间思考过程：\")\n",
    "print(message_2.content.events[-1].model_dump_json(indent=4))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**output**\n",
    "```\n",
    "Agent的中间思考过程：\n",
    "{\n",
    "    \"code\": 0,\n",
    "    \"message\": \"\",\n",
    "    \"status\": \"interrupt\",\n",
    "    \"event_type\": \"Interrupt\",\n",
    "    \"content_type\": \"contexts\",\n",
    "    \"detail\": {\n",
    "        \"text\": {\n",
    "            \"function_call\": {\n",
    "                \"thought\": \"根据execute_local_command工具的返回结果，当前文件夹下并没有'test.txt'文件。因此，我需要使用execute_local_command工具来执行'echo \\\"Hello World\\\" > test.txt'命令，以创建新的'test.txt'文件并写入内容'Hello World！'。\",\n",
    "                \"name\": \"execute_local_command\",\n",
    "                \"arguments\": {\n",
    "                    \"cmd\": \"echo \\\"Hello World\\\" > test.txt\"\n",
    "                },\n",
    "                \"usage\": {\n",
    "                    \"prompt_tokens\": 1040,\n",
    "                    \"completion_tokens\": 117,\n",
    "                    \"total_tokens\": 1157,\n",
    "                    \"name\": \"ERNIE-4.0-Turbo-8K\",\n",
    "                    \"type\": \"plan\"\n",
    "                },\n",
    "                \"tool_call_id\": \"9eaa3831-0d85-47eb-a39f-866ad2690c28\"\n",
    "            },\n",
    "            \"used_tool\": [\n",
    "...\n",
    "            }\n",
    "        }\n",
    "    ]\n",
    "}\n",
    "```\n",
    "\n",
    "- 大模型下发了调用本地函数的参数，我们使用这个参数调用本地函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "tool_call = message_2.content.events[-1].tool_calls[-1]\n",
    "tool_call_id = tool_call.id\n",
    "tool_call_argument = tool_call.function.arguments\n",
    "local_func_result = execute_local_command(**tool_call_argument)\n",
    "print(\"No.2 local_func_result: {}\\n\".format(local_func_result))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**output**\n",
    "```\n",
    "No.2 local_func_result: 命令执行成功，无返回值\n",
    "```\n",
    "\n",
    "- 向应用返回本地运行的结果，完成应用的调用"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "message_3 = app_client.run(\n",
    "    conversation_id=conversation_id,\n",
    "    tool_outputs=[{\n",
    "        \"tool_call_id\": tool_call_id,\n",
    "        \"output\": local_func_result\n",
    "    }]\n",
    ")\n",
    "\n",
    "event_status = message_3.content.events[-1].status\n",
    "if event_status != \"interrupt\":\n",
    "    print(\"Agent 的回答是：\\n\")\n",
    "    print(message_3.content.answer)\n",
    "    quit()\n",
    "\n",
    "print(\"Agent的中间思考过程：\")\n",
    "print(message_3.content.events[-1].model_dump_json(indent=4))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**output**\n",
    "```\n",
    "Agent的中间思考过程：\n",
    "{\n",
    "    \"code\": 0,\n",
    "    \"message\": \"\",\n",
    "    \"status\": \"interrupt\",\n",
    "    \"event_type\": \"Interrupt\",\n",
    "    \"content_type\": \"contexts\",\n",
    "    \"detail\": {\n",
    "        \"text\": {\n",
    "            \"function_call\": {\n",
    "                \"thought\": \"用户的需求是列出当前文件夹下的所有文件，并在没有test.txt文件的情况下创建一个新的test.txt文件，内容为'Hello World！'。根据之前的执行结果，我们已经成功创建了test.txt文件并写入了内容。现在，我需要再次使用execute_local_command工具来执行ls命令，以确认test.txt文件已经成功创建，并且列出当前文件夹下的所有文件，以满足用户的完整需求。\",\n",
    "                \"name\": \"execute_local_command\",\n",
    "                \"arguments\": {\n",
    "                    \"cmd\": \"ls\"\n",
    "                },\n",
    "                \"usage\": {\n",
    "                    \"prompt_tokens\": 1115,\n",
    "                    \"completion_tokens\": 133,\n",
    "                    \"total_tokens\": 1248,\n",
    "                    \"name\": \"ERNIE-4.0-Turbo-8K\",\n",
    "                    \"type\": \"plan\"\n",
    "                },\n",
    "                \"tool_call_id\": \"eaf51384-b48c-41d2-965a-ca57efb4a6f1\"\n",
    "            },\n",
    "            \"used_tool\": [\n",
    "                {\n",
    "                    \"function_call\": {\n",
    "                        \"thought\": \"首先，我需要使用execute_local_command工具来执行ls命令，列出当前文件夹下的所有文件。然后，我需要检查输出中是否存在test.txt文件。如果不存在，我将再次使用execute_local_command工具来执行echo命令，创建一个新的test.txt文件，并写入内容'Hello World！'。\",\n",
    "                        \"name\": \"execute_local_command\",\n",
    "                        \"arguments\": {\n",
    "                            \"cmd\": \"ls\"\n",
    "                        }\n",
    "                    },\n",
    "                    \"function_resp\": \"multi_tool_call.ipynb\\nmulti_tool_call.py\\nmulti_tool_call_with_handler.py\\nsdk_ knowledgebase.ipynb\\nsdk_trace.ipynb\\nsimple_tool_call.ipynb\\nsimple_tool_call.py\\ntmp.log\\n黑神话(悟空).pdf\\n\"\n",
    "                },\n",
    "                {\n",
    "                    \"function_call\": {\n",
    "                        \"thought\": \"根据执行结果，当前文件夹下并没有test.txt文件，所以需要使用execute_local_command工具来执行echo命令，创建一个新的test.txt文件，并写入内容'Hello World！'。\",\n",
    "                        \"name\": \"execute_local_command\",\n",
    "                        \"arguments\": {\n",
    "                            \"cmd\": \"echo 'Hello World！' > test.txt\"\n",
    "                        },\n",
    "                        \"usage\": {\n",
    "                            \"prompt_tokens\": 1025,\n",
    "                            \"completion_tokens\": 96,\n",
    "                            \"total_tokens\": 1121,\n",
    "                            \"name\": \"ERNIE-4.0-Turbo-8K\",\n",
    "                            \"type\": \"plan\"\n",
    "                        },\n",
    "                        \"tool_call_id\": \"f43bac97-667d-40f0-a138-c6ade008bdef\"\n",
    "                    },\n",
    "                    \"function_resp\": \"命令执行成功，无返回值\"\n",
    "                }\n",
    "            ]\n",
    "        }\n",
    "    },\n",
    "    \"usage\": null,\n",
    "    \"tool_calls\": [\n",
    "        {\n",
    "            \"id\": \"eaf51384-b48c-41d2-965a-ca57efb4a6f1\",\n",
    "            \"type\": \"function\",\n",
    "            \"function\": {\n",
    "                \"name\": \"execute_local_command\",\n",
    "                \"arguments\": {\n",
    "                    \"cmd\": \"ls\"\n",
    "                }\n",
    "            }\n",
    "        }\n",
    "    ]\n",
    "}\n",
    "```\n",
    "\n",
    "- 大模型下发了调用本地函数的参数，我们使用这个参数调用本地函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "tool_call = message_3.content.events[-1].tool_calls[-1]\n",
    "tool_call_id = tool_call.id\n",
    "tool_call_argument = tool_call.function.arguments\n",
    "local_func_result = execute_local_command(**tool_call_argument)\n",
    "print(\"No.3 local_func_result: {}\\n\".format(local_func_result))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**output**\n",
    "```\n",
    "No.3 local_func_result: multi_tool_call.ipynb\n",
    "multi_tool_call.py\n",
    "multi_tool_call_with_handler.py\n",
    "sdk_ knowledgebase.ipynb\n",
    "sdk_trace.ipynb\n",
    "simple_tool_call.ipynb\n",
    "simple_tool_call.py\n",
    "test.txt\n",
    "tmp.log\n",
    "黑神话(悟空).pdf\n",
    "```\n",
    "\n",
    "- 向应用返回本地运行的结果，完成应用的调用"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "message_4 = app_client.run(\n",
    "    conversation_id=conversation_id,\n",
    "    tool_outputs=[{\n",
    "        \"tool_call_id\": tool_call_id,\n",
    "        \"output\": local_func_result\n",
    "    }]\n",
    ")\n",
    "print(\"Agent 的回答是：\\n\")\n",
    "print(message_4.content.answer)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**output**\n",
    "```\n",
    "Agent 的回答是：\n",
    "\n",
    "当前文件夹下的文件包括：\n",
    "\n",
    "- multi_tool_call.ipynb\n",
    "- multi_tool_call.py\n",
    "- multi_tool_call_with_handler.py\n",
    "- sdk_knowledgebase.ipynb\n",
    "- sdk_trace.ipynb\n",
    "- simple_tool_call.ipynb\n",
    "- simple_tool_call.py\n",
    "- test.txt\n",
    "- tmp.log\n",
    "- 黑神话(悟空).pdf\n",
    "\n",
    "由于当前文件夹下没有test.txt文件，因此已经成功创建了一个新的test.txt文件，并写入了内容'Hello World！'。现在，test.txt文件已经出现在文件夹中。\n",
    "```\n",
    "\n",
    "我们就完成了一个基于Client与自定义tool工具完成了一个 \"请问当前文件夹下有哪些文件？如果没有test.txt文件，请新建一个test.txt文件，内容为：Hello World\" 的操作"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "python-12",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
